原文链接

写React的时候,你是应该用React.createClass语法还是ES6的class语法呢?或者都不?这篇文章将会解释一些二者之间的差异来帮助你做选择。

React可以用ES5或ES6完美书写。

使用JSX意味着你将通过Babel将JSX通过“build”转化为React.createElement调用。很多人利用这个特点在Babel的编译列表中添加一些es2015的语法以此来让ES6完全可用。

如果你正在使用Quik或是React Heatpack,这已经为你配置好了(如果你还没有安装环境,请阅读quick start React)。

比较 createClass 和 class

以下是使用React.createClass和ES6的class书写相同的组件:

var InputControlES5 = React.createClass({
propTypes: {
initialValue: React.PropTypes.string
},
defaultProps: {
initialValue: ''
}, // Set up initial state
getInitialState: function() {
return {
text: this.props.initialValue || 'placeholder'
};
},
handleChange: function(event) {
this.setState({
text: event.target.value
});
},
render: function() {
return (
<div>
Type something:
<input onChange={this.handleChange}
value={this.state.text} />
</div>
);
}
});

class InputControlES6 extends React.Component { 
constructor(props) {
super(props);
// Set up initial state
this.state = {
text: props.initialValue || 'placeholder'
};
// Functions must be bound manually with ES6 classes
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
text: event.target.value
});
}
render() {
return (
<div>
Type something:
<input onChange={this.handleChange}
value={this.state.text} />
</div>
);
}
}
InputControlES6.propTypes = {
initialValue: React.PropTypes.string
};
InputControlES6.defaultProps = {
initialValue: ''
};

以下是一些关键的不同点:

函数的绑定

这或许是最大的一个变化点

使用createClass, 每一个函数属性都会被React自动绑定。指的是this.whateverFn这样的函数在任何你需要调用的时候都会自动为你绑定正确的this

在ES6的class中,就不同了: 函数不再被自动绑定。你需要手动去绑定它们。最好的地方就是和以上例子一样,在构造函数里。

如果你不想非要手动打印所有的绑定操作,也可以通过react-autobind或者autobind-decorator来检查。

另一种方式就是在你使用的地方通过内联来绑定:

// Use `.bind`:
render() {
return (
<input onChange={this.handleChange.bind(this)}
value={this.state.text} />
);
}
// --- OR ---

// Use an arrow function:
render() {
return (
<input onChange={() => this.handleChange()}
value={this.state.text} />
);
}

以上任意一种都可以,然而在效率上却不行了。每一次调用render(可以说是非常频繁!)一个新的函数都会被创建。与在构造函数里只绑定一次相比就慢一些。

最终的选择是使用箭头函数直接替换函数在类中的声明,像这样:

// the normal way
// requires binding elsewhere
handleChange(event) {
this.setState({
text: event.target.value
});
}
// the ES7 way
// all done, no binding required
handleChange = (event) => {
this.setState({
text: event.target.value
});
}

通过这种方式,你不需要绑定任何东西。这都已经通过神奇的箭头函数被搞定了。像期望的那样,函数内部的this将会指向组件实例。

唯一的注意的地方就是这仅是一个”试验性” 的特性,意味着这不是官方的ES6规范。但是这依旧可以通过设置babel为”stage-0”来支持。如果你喜欢这种语法(被称作“把handleChange设置为带event的箭头函数”),试试吧。

构造函数应该调用super

ES6类的constructor需要接收props并且调用super(props)。这是createClass所没有的一点。

class和createClass的比较

这个很明显,一个调用React.createClass并传入一个对象,另一个则是使用class继承React.Component

小提示: 导入Component时如果在一个文件里包含多个组件,可以直接通过以下方式节省一些代码量:
import React, {Component} from 'react'

初始化State的设置

createClass 接受一个只会在组件被挂载时才会调用一次的initialState函数。

ES6 的class则使用构造函数。在调用super之后,可以直接设置state。

propTypes 和 defaultProps 的设置

使用createClass时,在传入的对象上定义一个propTypesdefaultProps作为属性。

使用ES6的类,这些将会成为类本身的属性,因此它们可以在类定义之后补充上。

如果你开启了ES7属性初始化,这里还有些捷径:

class Person extends React.Component { 
static propTypes = {
name: React.PropTypes.string,
age: React.PropTypes.string
};
static defaultProps = {
name: '',
age: -1
};
...
}

第三种选择

createClassclass以外,React也支持“无状态函数式组件”。基本上,这仅是一个不能有state的函数,并且它不能使用像componentWillMountshouldComponentUpdate这样的任何生命周期方法。无状态函数式组件对仅仅接受并依赖props渲染的简单组件来讲很棒,以下是几个例子:

function Person({firstName, lastName}) { 
return (
<span>{lastName}, {firstName}</span>
);
}

这利用ES6的解构将传入的props分解开来,但也可以这么写:

function Person(props) { 
var firstName = props.firstName;
var lastName = props.lastName;
return (
<span>{lastName}, {firstName}</span>
);
}

用哪个才对呢?

Facebook已经声明了React.createClass将最终会被ES6的classes替代,但是他们也说“在我们找到当前mixin所使用的例子的替代者以及在语言上支持类属性的初始化器前,我们不打算废弃React.createClass”。

在任何可以使用无状态函数式组件的地方使用它。简单而且会强制性地简化你的组件。

对于一些需要state,生命周期方法或是通过ref来操作潜在的DOM节点的复杂组件来讲,请使用class。

尽管知道这三种写法风格很棒,但在StackOverflow或者其他地方查找解决方案时可能仍会看到一些混杂着ES5和ES6写法的答案。ES6风格已经非常流行了但这不会是你看到的唯一一种写法。

累死宝宝了,在翻译的过程中,自己也在宏观上了解了一些东西,希望能够帮助更多英语不好的同学们。
——译者注

donation